﻿# -*- coding: utf-8 -*-
# TaoSync.py

# Copyright 2012 Ryan Schultz

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>

import urllib2
import zipfile
import shutil
import os
import sys
import hashlib

import Manifest
import DirHasher

class TaoSync:
	"""Downloads a manifest file and syncs its contents to the current directory."""
	def __init__(self, file_name):
		self.file_name = file_name
		self.download_path = ""
		self.address = ""
		self.servername = ""
		
		self.progress_increment = 0
		self.didProgress = False
		
		self.download_percent = 0
		self.download_file = ""
		
		self.terminate_download = False
		
		try:
			print "INFO: Downloading manifest %s..." % file_name
			manifest_data = urllib2.urlopen(file_name).read()
		except:
			print "ERROR: Could not download manifest."
			raise
		
		self.manifest_data = manifest_data
		
	# Utility functions for downloading with a progress bar

	def chunk_rw(self, response, file_name, chunk_size=1048576):
		total_size = response.info().getheader('Content-Length').strip()
		total_size = int(total_size)
		bytes_so_far = 0

		f = open(file_name, "wb")
	   
		while not self.terminate_download:
			chunk = response.read(chunk_size)
			f.write(chunk)
			bytes_so_far += len(chunk)

			if not chunk:
				break

			percent = float(bytes_so_far) / total_size
			percent = round(percent*100, 2)
			print ("Downloaded %d of %d bytes (%0.2f%%)\r" % (bytes_so_far, total_size, percent))
			self.download_percent = percent

			if bytes_so_far >= total_size:
				self.download_percent = 100

		return f
		
	def download_addon(self, addon, ziphash):
		# Try to download the addon
		try:
			print "INFO: Downloading addon %s..." % addon
			self.download_percent = 0
			self.download_file = self.address + addon + ".zip"
			uo = urllib2.urlopen(self.address + addon + ".zip")
			addon_zip = self.chunk_rw(uo, addon + ".zip")
			addon_zip.close()
			
			# Check the addon's hash
			SHAhash = hashlib.sha1()
			try:
				zf = open(addon + ".zip", "rb")
				# Now hash it
				zf_hash = hashlib.sha1(zf.read()).hexdigest()
						
			except:
				raise
			
			finally:
				zf.close()
			
			if zf_hash == ziphash:
				print "INFO: Download complete and hash matched!"
			else:
				print "WARN: Download complete but hash does not match!"
				raise
			
			print "INFO: Downloaded %s." % addon
		except:
			print "ERROR: Failed to download %s!" % addon
			raise IOError('Downloaded file hash was incorrect.')
			
	def extract_addon(self, file_name):
		# Extract the archive
		try:
			print "INFO: Extracting %s..." % (file_name + ".zip")
			zf = open(file_name + ".zip", 'rb')
			zipper = zipfile.ZipFile(zf, "r")
			zipper.extractall()
		except:
			print "ERROR: Unzip failed!"
			raise
		finally:
			zf.close()
	
	def delete_addon(self, file_name):
		try:
			#shutil.rmtree(file_name)
			print 'fake delete'
		except:
			print "ERROR: Unable to delete addon folder %s!" % file_name
		
		
	def sync(self, checkonly = False):
		mf = Manifest.Manifest()
		mf.read_manifest(self.manifest_data)
		self.download_path = os.getcwd()
		self.servername = mf.servername
		self.address = mf.address
		
		did_update = False
		
		print "INFO: Syncing addons for server %s" % self.servername
		
		dh = DirHasher.DirHasher()
		
		mf_dict = mf.manifest_dictionary
		
		import time
		self.progress_increment = 100 / len(mf_dict)
		for addon in mf_dict:
			ad = mf_dict[addon]
			ad_dirhash = ad[0]
			ad_ziphash = ad[1]
			
			if not os.path.exists(addon): # First see if we have it all
				print "INFO: Addon %s does not exist." % addon
				did_update = True
				if not checkonly:
					try:
						self.download_addon(addon, ad_ziphash)
						self.extract_addon(addon)
					except:
						print "ERROR: Download and extract failed."
						
			elif dh.make_dir_hash(addon) != ad_dirhash: # We do have it, so check if they match
				# Since they don't match, delete the existing one and download the new one
				print "UPDATE: A new or changed version has been released of %s" % addon
				did_update = True
				if not checkonly:
					self.delete_addon(addon)
					self.download_addon(addon, ad_ziphash)
					self.extract_addon(addon)
			else:
				print "INFO: %s is up to date." % addon
			
			self.didProgress = True
		
		print "INFO: Sync sequence ended."
		return did_update
			
	
if __name__ == "__main__":
	ts = TaoSync("file:///C:/Users/Ryan%20Schultz/Workspace/TaoSync/TestDirectory/test_manifest.xml")
	ts.sync()
	print ""
	print "Sync complete. You can delete the zip files if you'd like."